// C source file with the function definitions to handle CSV files

// count_csv_rows() function
// function to count the rows of a CSV file loaded in memory
umax count_csv_rows(void * src, umax src_size)
{
  // check params
  if (src == NULL || src_size == 0)
    goto err;
  
  // variable to return
  umax row_cnt = 0;
  byte * ptr = src;
  for (umax i = 0; i < src_size; /**/) {
    ptr = find_chint_in_chenc_arr((byte *) src + i, src_size - i, , ENC_NEWLN);
    // if no newline is read then the end of the file has been reached
    if (ptr == NULL)
      break;
    // advance to the next character
    i = ptr - (byte *) src + chk_ch_int(ENC_NEWLN);
    row_cnt++;
  }
  
  // done!
  return row_cnt;  
  // failure
  err:
  return 0;
}

// count_csv_row_cols() function
// function to count the columns of a row of a CSV file loaded in memory
umax count_csv_row_cols(void * src, umax src_size)
{
  // check params
  if (src == NULL || src_size == 0)
    goto err;
  
  // variable to return
  umax col_cnt = 0;
  byte ch_size = 0;
  // get the end of the row of the CSV
  byte * ptr1 = src, * ptr2 = find_chint_in_chenc_arr(src, src_size, ENC_NEWLN);
  if (ptr2 == NULL)
    goto err;
  
  // count the columns on the current row
  for (umax i = 0; ptr1 != NULL; i = chk_ch_int(ENC_COMMA) + ptr1 - (byte *) src) {
    ptr1 = find_chint_in_chenc_arr((byte *) src + i,  ptr2 - (byte *) src - i, ENC_COMMA);
    col_cnt++;
  }
  
  // done!
  return col_cnt;  
  // failure
  err:
  return 0;
}

// count_csv_mx_cols() function
// function to count the maximum number of columns of a CSV file loaded in memory
umax count_csv_mx_cols(void * src, umax src_size)
{
  // check params
  if (src == NULL || src_size == 0)
    goto err;
  
  // get the maximum number of columns
  byte * ptr = src;
  umax col_cnt = 0, max_col_cnt = 0;
  while (ptr < (byte *) src + src_size)
  {
    // get the current row col count
    col_cnt = count_csv_row_cols(ptr, (byte *) src + src_size - ptr);
    if (col_cnt == 0)
      goto err;
    // make the pointer to advance to the ENC_NEWLN
    ptr = find_chint_in_chenc_arr(ptr, (byte *) src + src_size - ptr, ENC_NEWLN) + chk_ch_int(ENC_NEWLN);
    // update max_col_cnt
    max_col_cnt = get_max_uint(max_col_cnt, col_cnt);
  }
  
  // done!
  return max_col_cnt;
  // failure
  err:
  return 0;
}

// check_text_csv() function
// function to check if a given text file is a valid CSV
text_t check_text_csv(text * text_file)
{
  // check the inc error code and if the text struct has been already identified
  if (text_file -> ERR_CD_INC > TEXT_ERR_LN || text_file -> TYPE != TEXT_NO_TYPE)
    return TEXT_NO_TYPE;
  // iirc no more checks are necessary
  return TEXT_CSV;
}

// open_text_csv() function
// function to open a valid CSV file
text * open_text_csv(char * text_csv_file_path)
{
  // check if the file is valid
  text * file = open_text_inc(text_csv_file_path);
  if ((file -> TYPE = check_text_csv(file)) == TEXT_NO_TYPE) {
    close_text_inc(file);
    goto err;
  }
  
  // build a complete CSV structure
  file -> ST = allocate_memory(1, sizeof(text_csv));
  
  // fill the structure
  text_csv * csv = file -> ST;
  
  // temp variables
  byte * ptr = file -> DATA;
  umax tmp = 0, tmp2 = 0;
  
  // get ROW_CNT and MX_COL_CNT
  csv -> ROW_CNT = count_csv_rows(ptr, file -> SIZE);
  csv -> MX_COL_CNT = count_csv_mx_cols(ptr, file -> SIZE);
  // allocate space for ROW_COL_CNT
  csv -> ROW_COL_CNT = allocate_memory(csv -> ROW_CNT, sizeof(umax));
  if (csv -> ROW_COL_CNT == NULL) goto err;
  
  // fill ROW_COL_CNT array
  for (umax i = 0; i < (csv -> ROW_CNT); i++) {
    tmp = (file -> DATA) + (file -> SIZE) - ptr;
    (csv -> ROW_COL_CNT)[i] = count_csv_row_cols(ptr, tmp);
    ptr = find_chint_in_chenc_arr(ptr, tmp, ENC_NEWLN) + chk_ch_int(ENC_NEWLN);
  }
  
  // allocate space for ELEMS (ROW_CNT x MX_COL_CNT matrix)
  csv -> ELEMS = allocate_memory(csv -> ROW_CNT, sizeof(text_csv_elem *));
  if (csv -> ELEMS == NULL) goto err;
  // fill ROW_COL_CNT 2d array
  ptr = file -> DATA;
  for (umax i = 0; i < (csv -> ROW_CNT); i++)
  {
    // allocate space for each (csv -> ELEMS)[i]
    (csv -> ELEMS)[i] = allocate_memory(csv -> MX_COL_CNT, sizeof(text_csv_elem));
    if ((csv -> ELEMS)[i] == NULL) goto err;
    for (umax j = 0; j < (csv -> ROW_COL_CNT)[i]; j++) // fill (csv -> ELEMS)[i]
    {
      // skip whitespace     
      tmp = (file -> DATA) - ptr + (file -> SIZE);
      ptr = ign_text_whtsp(ptr, tmp);
      
      // assign pointer to element if not empty
      // then assign its state to either TEXT_CSV_CELL_EMPTY
      // or leave it as TEXT_CSV_NO_CELL (value 0)
      (csv -> ELEMS)[i][j].PTR = ptr;
      tmp2 = get_ch_int(ptr, tmp);
      if (tmp2 != ENC_COMMA && tmp2 != ENC_NEWLN)
        (csv -> ELEMS)[i][j].STATE = TEXT_CSV_ELEM_EXISTS;
      else
        (csv -> ELEMS)[i][j].STATE = TEXT_CSV_CELL_EMPTY;
    
      // advance to the next element/row
      if (j < (csv -> ROW_COL_CNT)[i] - 1)
        ptr = find_chint_in_chenc_arr(ptr, tmp, ENC_COMMA) + chk_ch_int(ENC_COMMA);
      else
        ptr = find_chint_in_chenc_arr(ptr, tmp, ENC_NEWLN) + chk_ch_int(ENC_NEWLN);
    }
  } 
  
  // all went good
  return file;
  // nuhuh
  err:
  return NULL;
}

// close_text_csv() function
// function to close a complete text CSV structure (free memory)
void close_text_csv(text * text_file)
{
  // check params
  if (text_file == NULL || (text_file -> TYPE) != TEXT_CSV)
    return;
  
  // get CSV section pointer
  text_csv * csv = text_file -> ST;
  
  // free the arrays from (csv -> ELEMS)[i]
  for (umax i = 0; i < csv -> ROW_CNT; i++)
    free_memory((csv -> ELEMS)[i]);
  // free the csv -> ELEMS pointer array
  free_memory(csv -> ELEMS);
  // free the csv -> ROW_COL_CNT array
  free_memory(csv -> ROW_COL_CNT);
  // free the CSV structure from text_file
  free_memory(text_file -> ST);
  // release the rest of the generic
  // text structure memory usage
  close_text_inc(text_file);
  
  // all done
  return;
}

// print_text_csv() function
// print the information of a CSV file
void print_text_csv(text * file)
{
  // check params
  if (file == NULL)
    return;
  
  // starting visual queue
  printf("\n### CSV struct info start ###\n\n");
  
  // print the start of the text struct
  print_text_start(file);
  
  // get csv struct pointer
  text_csv * csv = file -> ST;
  
  // print CSV element distribution table
  printf("\nCSV Element distribution table:\n");
  printf("\"+\" means that the element exists\n");
  printf("\" \" means that the cell exists but it is empty\n");
  printf("\"-\" means that the cell does not exist\n");
  for (umax i = 0; i < (csv -> ROW_CNT); i++)
  {
    printf("Row %llu:", i + 1);
    for (umax j = 0; j < (csv -> MX_COL_CNT); j++)
      if ((csv -> ELEMS)[i][j].STATE == TEXT_CSV_ELEM_EXISTS)
        printf(" | +");
      else if ((csv -> ELEMS)[i][j].STATE == TEXT_CSV_CELL_EMPTY)
        printf(" |  ");
      else
        printf(" | -");
    printf(" |\n");
  }
  
  // end of structure
  printf("\n### CSV struct info end ###\n");
  return;  
}

